-- Monster
-- Create by weism
-- 怪物对象

Monster = {
    -- 怪物编号
    classId = 0,

    -- 怪物数据
    dbase = {},

    -- 实体类型
    type   = OBJECT_TYPE_MONSTER,
};
Monster.__index = Monster;

-- 构造函数
function Monster.new(classId, dbase)
    local self = {};
    setmetatable(self, Monster);
    self.classId = classId;
    dbase["class_id"] = classId;
    self.dbase = Dbase.new(dbase);
    self.style = self:query("style");

    if dbase.hp then
        self.dbase:set("max_hp", dbase.hp);
    end

    return self;
end

-- 取得该对象的描述字符串
function Monster:get_ob_id()
    return string.format("Monster:%s", tostring(self.classId));
end

-- 名字
function Monster:getName()
    return self:query("name") or "anonymous";
end

-- 设置classId
function Monster:setClassId(classId)
    self.classId = classId;

    self.dbase:set("class_id", classId);
end

-- classId
function Monster:getClassId()
    -- 优先用dbase下的
    return self.dbase:query("class_id", self.classId);
end

-- 查找数据
function Monster:query(path, path2, default)
    if path == "classId" or path == "class_id" then
        return self.dbase:query("class_id");
    else
        -- 先从dbase读取，如果读取不到，则从配置表中读取
        local value = self.dbase:query(path, path2, default);
        if nil ~= value then
            return value;
        end

        local ret = MonsterM.query(self.classId, path);
        if ret then
            return ret;
        end

        local dbase = MonsterM.query(self.classId, "dbase") or {};
        return dbase[path];
    end
end

-- 判断是否是boss
function Monster:isBoss()
    return MonsterM.query(self.classId, "monster_type") == MONSTER_TYPE_BOSS;
end

-- 是否是冈布奥怪
function Monster:isSlime()
    return MonsterM.query(self.classId, "monster_type") == MONSTER_TYPE_SLIME;
end

-- 是否是观众怪
function Monster:isAudience()
    return MonsterM.query(self.classId, "monster_type") == MONSTER_TYPE_AUDIENCE;
end

-- 是否已死亡
function Monster:isDead()
    -- 有死亡标志
    if 1 == self:query("is_dead") then
        return true;
    end

    return false;
end

-- 查询属性
function Monster:queryAttrib(attrib, args)
    --print("Monster:queryAttrib:"..attrib);
    return AttribM.queryAttrib(self, attrib, args);
end

-- 计算最大血量
function Monster:getHp()
    return self:queryAttrib("hp");
end

-- 最大血量
function Monster:getMaxHp()
    return self:queryAttrib("max_hp");
end

-- 阶位（精英）
function Monster:getRank()
    return self:query("rank") or 0;
end

-- 设置当前血量
function Monster:setHp(hp)
    self.dbase:set("hp", hp);
end

-- 获取真正的血量(想不到比较好的方案，暂时恶心做法)
-- 如果有辅助属性，那么就返回经过辅助后的血量，否则返回false
function Monster:getRealHp()
    local aid_attrib = self.dbase:queryTemp("aid_attrib") or {};
    local m = aid_attrib.hp;
    local prop = PropM.combine(self, "aid_attrib", "hp");

    -- 建筑辅助
    local bProp = PropM.getBuildingAidsProp(self, "aid_attrib", "hp");
    if type(bProp) == "table" and bProp[3] > 0 then
        prop[3] = prop[3] + bProp[3];
    end

    -- 1. 如果当前没有加成
    if not m then
        -- 当前有辅助属性
        local hp = self.dbase:query("hp", 0);
        if (prop[3] > 0) then
            hp = PropM.apply(prop, hp);

            self.dbase:setTemp("aid_attrib", "hp", {["hp"] = hp, ["co"] = prop[3]});

            return hp;
        end

        return false;
    else
        -- 有辅助血量，需要看看当前是否还有辅助属性
        if (prop[3] > 0) then
            -- 直接返回辅助血量
            return m["hp"];
        end

        -- 没有辅助了，直接清除
        self.dbase:deleteTemp("aid_attrib", "hp");

        return false;
    end
end

-- 设置真正血量
function Monster:setRealHp(hp)
    -- 属性加成
    local aid_attrib = self.dbase:queryTemp("aid_attrib") or {};
    local m = aid_attrib.hp;

    -- 精英棋子减少的
    local elite_reduce = self.dbase:queryTemp("elite_reduce") or {};
    local n = elite_reduce.hp;

    if not m and not n then
        hp = hp - (self:getHp() - self.dbase:query("hp", 0));

        self:setHp(hp);
        return;
    end

    local m_co = 0;
    local n_co = 0;

    if n then
        -- 削弱的
        n["hp"] = hp;
        n_co = n["co"];
    end

    if m then
        -- 加成的
        m["hp"] = extraCeil(1000 * hp, 1000 - n_co);
        m_co = m["co"];
    end

    -- baseValue * 加成(1+add) * 削弱(1-reduce) = attribValue
    local co = FormulaM.invoke("CALC_BIG_DIVISION", m_co + 1000, 1000 - n_co, 1000) - 1000;
    local baseHp = extraCeil(1000 * hp, 1000 + co);
    -- local baseHp = math.ceil( 1000 * hp / (1000 + m["co"]) );
    self:setHp(baseHp);
    return hp;
end

-- 获得属性
function Monster:addAttrib(attrib, amount)
    -- 如果是血量
    if "hp" == attrib then
        self:setRealHp(self:queryAttrib("hp") + amount);

        EventMgr.fire(event.MONSTER_INFO_UPDATED, { ["pos"] = self:getPos(), });

        return;
    end

    local curValue = self.dbase:query(attrib, 0);
    curValue = curValue + amount;
    self.dbase:set(attrib, curValue);

    EventMgr.fire(event.MONSTER_INFO_UPDATED, { ["pos"] = self:getPos(), });
end

-- 获取怪物所属的格子
function Monster:getOwner()
    return self.dbase:query("owner");
end

-- 位置
function Monster:getPos()
    local owner = self:getOwner();

    if type(owner) == "number" then
        return owner;
    end

    if type(owner) == "table" then
        return owner:getPos();
    end

    assert(false, "该怪物不是格子上刷出来的？");
end

-- 计算基础攻击值
function Monster:getAttack()
    return self:queryAttrib("attack");
end

-- 命中
function Monster:getAccuracy()
    return self:queryAttrib("accuracy");
end

-- 获取所有技能
function Monster:getAllSkills()
    return self.dbase:query("skills", {});
end

-- 闪避
function Monster:getDodge()
    return self:queryAttrib("dodge");
end

-- 受到伤害
function Monster:receiveDamage(damage)
    local hp = self:getHp() - damage;
    self:setRealHp(hp);

    local fetchProps = PropM.fetchProps;
    local trigger = PropM.trigger;

    -- TODO: 嗜血
    local props = fetchProps(self, "bloodthirsty");
    for _, prop in ipairs(props) do
        trigger(self, prop[1], prop[2]);
    end

    -- 受到伤害时使用技能
    local props = fetchProps(self, "wounded_magic");
    for _, prop in ipairs(props) do
        trigger(self, prop[1], prop[2]);
    end

    -- 受创掉落果实
    props = fetchProps(self, "fruits_drop");
    for _, prop in ipairs(props) do
        trigger(self, prop[1], prop[2]);
    end

    -- 尝试触发变形
    props = fetchProps(self, "hp_to_transform");
    for _, prop in ipairs(props) do
        trigger(self, prop[1], prop[2]);
    end

    --  生命低于%，尝试触发技能
    props = fetchProps(self, "hp_to_skill");
    for _, prop in ipairs(props) do
        trigger(self, prop[1], prop[2]);
    end

    -- 触发建筑装换
    props = fetchProps(self, "hp_trans_building");
    for _, prop in ipairs(props) do
        trigger(self, prop[1], prop[2]);
    end

    local msg = string.format("[Monster]%s(%d)收到伤害：%d，当前hp：(%s/%s)，max_hp：(%s/%s)",
        self:getName(), self:getPos(), damage, hp, self.dbase:query("hp"),
        self:getMaxHp(), self.dbase:query("max_hp"));
    DungeonLogM.addLog(msg);

    -- 通知受到伤害
    MonsterM.notifyReceiveDamage(self, {});

    return hp;
end

-- 倒计时
function Monster:getCountDown()
    local props  = PropM.fetchProps(self, "remote_magic");
    local props2 = PropM.fetchProps(self, "remote_attack");
    local props3 = PropM.fetchProps(self, "suicide_attack");
    local props4 = PropM.fetchProps(self, "flee");
    local props5 = PropM.fetchProps(self, "remote_magic2");
    local prop;

    -- 光环类的回合不需要显示
    if #props > 0 then
        local ret = {};
        for _, perProp in ipairs(props) do
            local skillId = perProp[3];
            local dbase = SkillM.query(skillId, "dbase") or {};
            if dbase["no_cd"]  ~= 1 then
                table.insert(ret, perProp);
            end
        end
        props = ret;
    end

    if #props > 0 then
        prop = props[1];
    elseif #props2 > 0 then
        prop = props2[1];
    elseif #props3 > 0 then
        prop = props3[1];
    elseif #props4 > 0 then
        prop = props4[1];
    elseif #props5 > 0 then
        prop = props5[1];
    else
        return;
    end

    -- 如果是拆炮属性，需要判断下当前是否还有炮台
    if self.notCD then
        return;
    end
    if prop[1] == PropM.getPropId("remote_magic") and prop[3] == 67 then
        -- 如果是没有炮台了
        local ok = false;
        for pos = 1, GRID_SIZE do
            local grid = DungeonM.getGridByPos(pos);
            if  grid.type == GRID_TYPE_TRAP and CommonTrapM.query(grid.id, "type") == TRAP_TYPE_CANNON and
                grid.state ~= GRID_STATE_DISABLE then
                ok = true;
                break;
            end
        end
        if not ok then
            -- 已经没有炮台了
            self.notCD = true;
            return;
        end
    end

    -- 如果是召唤回合有限制的
    local summonTimes = self.dbase:query("summon_times");
    local maxTimes = self.dbase:query("max_summon_times");
    if summonTimes and maxTimes and summonTimes >= maxTimes then
        self.notCD = true;
        return;
    end

    -- TODO: 当前回合待机
    local round = CombatM.getRound() - 1;
    local grid       = DungeonM.getGridByPos(self:getPos());
    local startRound = grid.openRound or 0;
    local cd = (round - startRound) % prop[2];

    -- 如果是逃跑倒计时
    if prop[1] == PropM.getPropId("flee") then
        return iif(tonumber(prop[2]) > round - startRound, prop[2] - cd, 1);
    end

    return prop[2] - cd;
end

-- 复活触发
function Monster:doReviveTrigger()
    DungeonLogM.addLog("[Monster]怪物死亡复活。");

    -- 遍历所有复活触发的属性
    for _, propId in ipairs(PropM.getTriggerProps("revive")) do
        local props = PropM.fetchProps(self, propId);
        for _, prop in ipairs(props) do
            PropM.trigger(self, prop[1], prop[2]);
        end
    end

    -- 标志已经复活过了
    self.hasRevived = true;

    -- 删除辅助信息
    self.dbase:deleteTemp("aid_attrib", "hp");
end

-- 怪物死亡
function Monster:die()
    if self:getHp() > 0 then
        return false;
    end

    local combine = PropM.combine;
    local apply = PropM.apply;
    local fetchProps = PropM.fetchProps;
    local trigger = PropM.trigger;

    -- 尝试复活
    if trigger(self, "revive", 1) then
        self:doReviveTrigger();

        return false;
    end

    -- 可复活一次属性
    local prop = combine(self, "revive_limit", 1);
    local hp = apply(prop, self:getMaxHp());
    if not self.hasRevived and hp > 0 then
        self:revive(hp);

        self:doReviveTrigger();

        return false;
    end

    -- 复活光环
    prop = combine(self, "revive_aura", 1);
    hp = apply(prop, self:getMaxHp());
    if not self.hasRevived and hp > 0 then
        self:revive(hp);

        self:doReviveTrigger();

        return false;
    end

    -- 无限复活光环
    prop = combine(self, "revive_aura1", 1);
    hp = apply(prop, self:getMaxHp());
    if hp > 0 then
        self:revive(hp);

        self:doReviveTrigger();

        return false;
    end

    -- 死亡转换
    local props = fetchProps(self, "die_transform");
    for _, prop in ipairs(props) do
        if trigger(self, prop[1], prop[2]) then
            return false;
        end
    end

    -- 已经死了
    if self:isDead() then
       trace("Monster", "怪物%s已经死了，不可以再死啊。", self:query("name") or "");
       return false;
    end

    local msg = string.format("[Monster]怪物%s死亡了", self:query("name") or "");
    DungeonLogM.addLog(msg);

    -- 标记死亡
    self.dbase:set("is_dead", 1);

    MonsterM.notifyDeath(self);

    return true;
end

-- 怪物逃脱
function Monster:escape()
    local msg = string.format("[Monster]怪物%s逃脱了", self:query("name") or "");
    DungeonLogM.addLog(msg);

    -- 标记死亡
    self.dbase:set("is_dead", 1);
    -- 标记为逃跑了
    self.dbase:set("flee", 1);

    self:setHp(0);

    -- 尝试清怪处理
    DungeonServiceM.monsterClear();

    return true;
end

-- 怪物复活
function Monster:revive(hp)
    -- 移除死亡标识
    self.dbase:set("is_dead", 0);

    -- TODO 恢复血量
    self:setHp(hp);

    local msg = string.format("[Monster]怪物%s复活了，当前血量%s，最大血量%s。", self:query("name") or "", hp, self:getMaxHp());
    DungeonLogM.addLog(msg);

    -- 抛出怪物复活事件
    EventMgr.fire(event.MONSTER_REVIVE, {["pos"] = self:getOwner(), });

    -- 删除辅助信息
    self.dbase:deleteTemp("aid_attrib", "hp");
end

-- 掉落
function Monster:dropProperty(source, drop)
    -- 如果怪物还未死亡
    if not self:isDead() then
        return false;
    end

    local grid = DungeonM.getGridByPos(self:getOwner());
    local base_class_id;
    local prop;
    local props;
    local bonus;

    -- 如果该格子有钥匙
    if grid.has_key then
        return false;
    end

    -- 本来就有物品
    if grid.bonus then
        -- 多倍掉落
        prop = PropM.combine(self, "multi_drop", source.type);

        -- 如果是食人花只有黑棘种子双倍掉落
        base_class_id = self:query("base_class_id");
        if (base_class_id == 34) then
            if (tonumber(grid.bonus[2]) == 7010) then
                grid.bonus[3] = PropM.apply(prop, grid.bonus[3]);
            end
        elseif grid.type ~= GRID_TYPE_BOX then
            -- 如果不是宝箱怪，允许有多倍掉落
            grid.bonus[3] = PropM.apply(prop, grid.bonus[3]);
        end

        return true;
    end

    -- 被某种召唤物击杀必定掉落某物品
    prop = PropM.combine(self, "summon_kill_drop", source.classId);
    if prop[3] > 0 then
        bonus = {1, prop[3], 1};
        grid.bonus = bonus;
        -- 抛出事件
        EventMgr.fire(event.MONSTER_DROP_PROPERTY, { ["pos"] = self:getOwner(), });
        return true;
    end

    -- 场上存在某种怪物时掉落
    bonus = FormulaM.invoke("CALC_INFLUENCE_MONSTER_DROP", self);
    if sizeof(bonus) > 0 then
        grid.bonus = bonus;

        -- 抛出事件
        EventMgr.fire(event.INFLUENCE_MONSTER_DROP, {["who"] = self, ["bonus"] = bonus, });

        return true;
    end

    -- 如果没有指定必定掉落
    if not drop then
        props = PropM.fetchProps(source, "plunder");

        for _, prop in ipairs(props) do
            bonus = PropM.trigger(source, prop[1], prop[2]);
            if type(bonus) == 'table' and #bonus > 0 then
               grid.bonus = bonus;
               -- 抛出第三只手发动事件
               EventMgr.fire(event.THIRD_HAND_PLUNDER);

               -- 抛出事件
               EventMgr.fire(event.MONSTER_DROP_PROPERTY, { ["pos"] = self:getOwner(), });

               -- 抛出事件尝试完成：竞技场获得对手物品
               EventMgr.fire(event.GET_ARENA_ENEMY_ITEM, { ["pos"] = self:getOwner(), ["bonus"] = bonus, });
               return true;
            end
        end

        -- 击杀增加物品掉落
        prop = PropM.combine(source, "kill_add_drop", 1);
        bonus = PropM.trigger(source, prop[1], prop[2], self)
        if type(bonus) == 'table' and #bonus > 0 then
           grid.bonus = bonus;
           return true;
        end

        return false;
    end

    -- 交给公式计算掉落
    bonus = FormulaM.invoke("CALC_MONSTER_PROPERTY_DROPED", self);

    if not bonus or #bonus <= 0 then
        return false;
    end

    -- 多倍掉落
    local prop = PropM.combine(self, "multi_drop", source.type);
    if grid.type ~= GRID_TYPE_BOX then
        -- 如果不是宝箱怪，允许有多倍掉落
        bonus[3] = PropM.apply(prop, bonus[3]);
    end

    grid.bonus = bonus;

    -- 抛出事件
    EventMgr.fire(event.MONSTER_DROP_PROPERTY, { ["pos"] = self:getOwner(), });

    return true;
end

-- 无敌的
function Monster:isInvincible()
    return self.invincible == 1 or self:query("invincible") == 1;
end

-- 远程怪物
function Monster:isRemoteMonster()
    local arr = FormulaM.invoke("GET_ALL_REMOTE_PROPS");

    for _, alias in pairs(arr) do
        local props = PropM.fetchProps(self, alias);
        if #props > 0 then
            return true;
        end
    end
    return false;
end

-- 收集信息
function Monster:collectInfo()
    local ret = {};
    local monster = self;

    ret.prop = monster.dbase:queryTemp("prop");
    ret.hp   = monster.dbase:query("hp", 0);
    ret.max_hp = monster:query("max_hp");
    ret.attack = monster.dbase:query("attack", 0);
    ret.attack_addon = monster.dbase:queryTemp("attack_addon");
    ret.class_id = monster.classId;
    ret.aid_attrib = monster.dbase:queryTemp("aid_attrib");
    ret.addon_hp = monster.dbase:query("addon_hp", 0);
    ret.action = monster.action;
    ret.track = monster:query("track");
    ret.dragon = monster:query("dragon");
    ret.is_dead = monster:query("is_dead");
    ret.be_stolen = monster:query("be_stolen");
    ret.remote_can_not_lock = monster:query("remote_can_not_lock");
    ret.style = monster.style;
    ret.combat_status = monster.dbase:query("combat_status", {});

    if monster:query("immunity_times") then
        ret.immunity_times = monster:query("immunity_times");
    end

    if monster:query("dodge_times") then
        ret.dodge_times = monster:query("dodge_times");
    end

    -- TODO 复活标记
    ret.has_revived = monster.hasRevived;

    -- 血低于%，施放技能标记
    ret.hp_to_skill = monster.hpToSkill;

    -- 怪物阵亡统计
    ret.killed_count = monster.killed_count or 0;

    -- 召唤师召唤次数
    ret.summon_times = monster.dbase:query("summon_times");

    -- 是否显示要是
    ret.show_key = monster.dbase:query("show_key") or 0;

    return ret;
end

-- 还原信息
function Monster:restoreInfo(info)
    local monster = self;

    -- 属性还原
    if info.prop then
        local prop = info.prop;
        monster.dbase:setTemp("prop", prop);

        -- 整理下
        PropM.arrangeProps(monster);
    end

    -- 辅助属性
    if info.aid_attrib then
        monster.dbase:setTemp("aid_attrib", info.aid_attrib);
    end

    -- 血量
    monster.dbase:set("hp", tonumber(info.hp));

    monster.dbase:set("max_hp", tonumber(info.max_hp));

    monster.dbase:set("addon_hp", info.addon_hp or 0);

    monster.dbase:set("attack", info.attack);

    monster.dbase:setTemp("attack_addon", info.attack_addon);

    monster.dbase:set("is_dead", info.is_dead);

    monster.dbase:set("be_stolen", info.be_stolen);

    monster.dbase:set("remote_can_not_lock", info.remote_can_not_lock);

    CombatStatusM.updateStatus(monster, info.combat_status or {});

    if info.style then
        monster.style = info.style;
    end

    if info.action then
        monster.action = info.action;
    end

    if info.track then
        monster.dbase:set("track", info.track);
    end
    if info.dragon then
        monster.dbase:set("dragon", info.dragon);
    end

    -- 是否复活过
    if (info.has_revived) then
        monster.hasRevived = true;
    end

    -- 是否施放过技能
    if (info.hp_to_skill) then
        monster.hpToSkill = true;
    end

    -- 召唤师召唤次数
    if (info.summon_times) then
        monster.dbase:set("summon_times", info.summon_times);
    end

    monster.killed_count = info.killed_count or 0;

    if info.immunity_times then
        monster.dbase:set("immunity_times", info.immunity_times);
    end

    if info.dodge_times then
        monster.dbase:set("dodge_times", info.dodge_times);
    end

    if info.show_key then
        monster.dbase:set("show_key", info.show_key);
    end

    -- 属性表现需要的检查
    self:checkPauseBackPerformance();
end

-- 属性表现需要的检查
function Monster:checkPauseBackPerformance()
    local monster = self;

    if FormulaM.invoke("MONSTER_IS_STOPED", monster) then
        -- 时间停止
        monster.dbase:set("isPause", true);
    end

    local dbase = MonsterM.query(self.classId, "dbase");
    if FormulaM.invoke("CAN_MONSTER_CHANGE_MODEL", monster) then
        if #PropM.fetchProps(monster, "transfiguration") > 0 then
            -- 变形术
            if monster:isBoss() and dbase["slime_display"] ~= 1 then
                monster.dbase:set("changeClassId", 1);
            else
                monster.dbase:set("changeClassId", 2);
            end
        elseif #PropM.fetchProps(monster, "skeleton") > 0 then
            -- 骷髅
            local changeClassId = 4;
            if monster:isBoss() and dbase["slime_display"] ~= 1 then
                -- 神龙夹层仙人掌特殊处理
                if UnrealDungeonM.isInUnreal() and monster.classId == 900 then
                    changeClassId = 5;
                else
                    changeClassId = 3;
                end
            end
            monster.dbase:set("changeClassId", changeClassId);
        end
    end
end
